package model

import (
	. "api/util"
	"errors"
	"database/sql"
	"time"
	"fmt"
	"math/rand"
	"strconv"
	"strings"
	jwt "github.com/dgrijalva/jwt-go"
)

type (
	Vcode struct {
		Id           int    `json:"id"`
		Phone        string `json:"phone"`
		Vcode        string `json:"vcode`
		CreatedDate  int    `json:"created_date"`
		UpdatedDate  int    `json:"updated_date"`
	}
)

func MakeVcode(pwl int) string {
	chars := "1234567890"
	clen := float64(len(chars))
	ret := ""

	rand.Seed(time.Now().Unix())

	for i := 0; i < pwl; i++ {
	  rfi := int(clen * rand.Float64())
	  ret += fmt.Sprintf("%c", chars[rfi])
	}

	return ret
}

func validateSession(phone string, vcode string) error {
	sql0 := "SELECT vcode,updated_date FROM session WHERE phone = ?"
	stmt, err := db.Prepare(sql0)
	if err != nil {
		return err
	}

	var vcode0 string
	var updatedDate int

	err = stmt.QueryRow(phone).Scan(&vcode0, &updatedDate)
	if err != nil && err != sql.ErrNoRows {
		return err
	}

	if vcode0 != vcode {
		return errors.New("验证码错误")
	}

	if err == sql.ErrNoRows {
		return errors.New("验证码错误")
	}

	now := time.Now().Unix()
	if int(now) - updatedDate > 60 {
		return errors.New("验证码超时")
	}

	return nil
}

func (cm *Customer) Register(phone string, password string, master int, vcode string) (n int, cid int, pid string, token string, err error) {
	err = validateSession(phone, vcode)
	if err != nil {
		return n, cid, pid, token, err
	}

	sql1 := "SELECT id FROM promotion_officer WHERE phone = ?"
	stmt, err := db.Prepare(sql1)
	if err != nil {
		return n, cid, pid, token, err
	}

	var id int
	err = stmt.QueryRow(phone).Scan(&id)
	if err != nil && err != sql.ErrNoRows {
		return n, cid, pid, token, err
	}

	if err == sql.ErrNoRows {
		tx, err := db.Begin()
		if err != nil {
			tx.Rollback()
			return n, cid, pid, token, err
		}

		var mpid int
		if master != 0 {
			stmt0, err := tx.Prepare("SELECT promotion_id FROM customer WHERE id=?")
			if err != nil {
				tx.Rollback()
				return n, cid, pid, token, err
			}

			err = stmt0.QueryRow(master).Scan(&mpid)
			if err != nil {
				tx.Rollback()
				return n, cid, pid, token, err
			}
		}

		stmt1, err := tx.Prepare("INSERT promotion_officer SET phone=?, password=?, master=?, apprentices=?, withdrawable=?, income=?, created_date=?, updated_date=?, status=?")
		if err != nil {
			tx.Rollback()
			return n, cid, pid, token, err
		}

		_, err = stmt1.Exec(phone, password, mpid, "", 0, 0, time.Now().Unix(), time.Now().Unix(), 1)
		if err != nil {
			tx.Rollback()
			return n, cid, pid, token, err
		}

		stmt2, err := tx.Prepare("SELECT id FROM promotion_officer WHERE phone=?")
		if err != nil {
			tx.Rollback()
			return n, cid, pid, token, err
		}

		var newAddedId int
		err = stmt2.QueryRow(phone).Scan(&newAddedId)
		if err != nil {
			tx.Rollback()
			return n, cid, pid, token, err
		}

		if newAddedId == 0 {
			tx.Rollback()
			return n, cid, pid, token, errors.New("newAddedId wrong")
		}

		if mpid != 0 {
			var apprentices string

			stmt3, err := tx.Prepare("SELECT apprentices FROM promotion_officer WHERE id=?")
			if err != nil {
				tx.Rollback()
				return n, cid, pid, token, err
			}

			err = stmt3.QueryRow(mpid).Scan(&apprentices)
			if err != nil && err != sql.ErrNoRows {
				tx.Rollback()
				return n, cid, pid, token, err
			}

			var s []string
			if len(apprentices) > 0 {
				s = strings.Split(apprentices, ",")
			}

			newAddedIdStr := strconv.Itoa(newAddedId)

			s = append(s, newAddedIdStr)

			sString := strings.Replace(strings.Trim(fmt.Sprint(s), "[]"), " ", ",", -1)

			stmt4, err := tx.Prepare("UPDATE promotion_officer SET apprentices=? WHERE id=?")
			if err != nil {
				tx.Rollback()
				return n, cid, pid, token, err
			}

			_, err = stmt4.Exec(sString, mpid)
			if err != nil {
				tx.Rollback()
				return n, cid, pid, token, err
			}
		}

		stmt5, err := tx.Prepare("SELECT id FROM promotion_officer WHERE phone=?")
		if err != nil {
			tx.Rollback()
			return n, cid, pid, token, err
		}

		err = stmt5.QueryRow(phone).Scan(&pid)
		if err != nil {
			tx.Rollback()
			return n, cid, pid, token, err
		}

		stmt6, err := tx.Prepare("SELECT id FROM customer WHERE phone=?")
		if err != nil {
			tx.Rollback()
			return n, cid, pid, token, err
		}

		err = stmt6.QueryRow(phone).Scan(&cid)
		if err != nil && err != sql.ErrNoRows {
			tx.Rollback()
			return n, cid, pid, token, err
		}

		if err == sql.ErrNoRows {
			stmt7, err := tx.Prepare("INSERT customer SET phone=?, created_date=?, updated_date=?, is_promotion=?, promotion_id=?, source_user_id=?")
			if err != nil {
				tx.Rollback()
				return n, cid, pid, token, err
			}

			_, err = stmt7.Exec(phone, time.Now().Unix(), time.Now().Unix(), 1, pid, master)
			if err != nil {
				tx.Rollback()
				return n, cid, pid, token, err
			}
		} else {
			stmt7, err := tx.Prepare("UPDATE customer SET phone=?, updated_date=?, is_promotion=?, promotion_id=?, source_user_id=? WHERE id=?")
			if err != nil {
				tx.Rollback()
				return n, cid, pid, token, err
			}

			_, err = stmt7.Exec(phone, time.Now().Unix(), 1, pid, master, cid)
			if err != nil {
				tx.Rollback()
				return n, cid, pid, token, err
			}
		}

		stmt8, err := tx.Prepare("SELECT id FROM customer WHERE phone = ?")
		if err != nil {
			tx.Rollback()
			return n, cid, pid, token, err
		}

		err = stmt8.QueryRow(phone).Scan(&cid)
		if err != nil {
			tx.Rollback()
			return n, cid, pid, token, err
		}

		tx.Commit()

		tokenCliams := Claims{
			Id: uint(cid),
			StandardClaims: jwt.StandardClaims{
				ExpiresAt: time.Now().Unix() + 24 * 3600000,
			},
		}

		token, err = MakeCliamsToken(tokenCliams)

		_, err = redisCustomer()
		if err != nil {
			return n, cid, pid, token, err
		}

		_, err = redisPromotionOfficer()
		if err != nil {
			return n, cid, pid, token, err
		}

		return 1, cid, pid, token, nil
	}

	_, err = redisCustomer()
	if err != nil {
		return n, cid, pid, token, err
	}

	_, err = redisPromotionOfficer()
	if err != nil {
		return n, cid, pid, token, err
	}

	return 2, cid, pid, token, nil
}

func (cm *Customer) Login(phone string, password string) (id int, token string, err error) {
	stmt, err := db.Prepare("SELECT password, status FROM promotion_officer WHERE phone = ?")
	if err != nil {
		return id, token, err
	}

	var pPwd string
	var pStatus int
	err = stmt.QueryRow(phone).Scan(&pPwd, &pStatus)
	if err != nil && err != sql.ErrNoRows {
		return id, token, err
	}

	if err != nil && err != sql.ErrNoRows {
		return id, token, err
	}

	if err == sql.ErrNoRows || password != pPwd {
		return id, token, errors.New("用户不存在或密码错误")
	}

	if pStatus == 0 {
		return id, token, errors.New("因违规操作，您的账号暂时被锁定")
	}

	stmt2, err := db.Prepare("SELECT id FROM customer WHERE phone = ?")
	if err != nil {
		return id, token, err
	}

	var cid int
	err = stmt2.QueryRow(phone).Scan(&cid)
	if err != nil && err != sql.ErrNoRows {
		return id, token, err
	}

	if err == sql.ErrNoRows {
		return id, token, errors.New("账号不存在")
	}

	tokenCliams := Claims{
        Id: uint(cid),
        StandardClaims: jwt.StandardClaims{
            ExpiresAt: time.Now().Unix() + 24 * 3600000,
        },
    }

    token, err = MakeCliamsToken(tokenCliams)
	id = cid

	return id, token, nil
}

func (cm *Customer) ForgotPassword(phone string, password string, vcode string) error {
	err := validateSession(phone, vcode)
	if err != nil {
		return err
	}

	stmt, err := db.Prepare("SELECT id FROM promotion_officer WHERE phone = ?")
	if err != nil {
		return err
	}

	var pid int
	err = stmt.QueryRow(phone).Scan(&pid)
	if err != nil && err != sql.ErrNoRows {
		return err
	}
	if err == sql.ErrNoRows {
		return errors.New("用户不存在")	
	}

	stmt1, err := db.Prepare("UPDATE promotion_officer SET password=?, updated_date=? WHERE id=?")
	if err != nil {
		return err
	}

	_, err = stmt1.Exec(password, time.Now().Unix(), pid)
	if err != nil {
		return err
	}

	_, err = redisPromotionOfficer()
	if err != nil {
		return err
	}

	return nil
}

func (v *Vcode) SaveSession(phone string, vcode string) error {
	sql1 := "SELECT id FROM session WHERE phone = ?"
	stmt, err := db.Prepare(sql1)
	if err != nil {
		return err
	}

	var id int
	err = stmt.QueryRow(phone).Scan(&id)
	if err != nil && err != sql.ErrNoRows {
		return err
	}

	if err == sql.ErrNoRows {
		stmt1, err := db.Prepare("INSERT session SET phone=?, vcode=?, created_date=?, updated_date=?")
		if err != nil {
			return err
		}

		_, err = stmt1.Exec(phone, vcode, time.Now().Unix(), time.Now().Unix())
		if err != nil {
			return err
		}

		return nil
	}

	stmt2, err := db.Prepare("UPDATE session SET vcode=?, updated_date=? WHERE id=?")
	if err != nil {
		return err
	}

	_, err = stmt2.Exec(vcode, time.Now().Unix(), id)
	if err != nil {
		return err
	}

	return nil
}

func (cm *Customer) AuthUser(id int, phone string) error {
	sql1 := "SELECT * FROM customer WHERE phone = ?"
	stmt, err := db.Prepare(sql1)
	if err != nil {
		return err
	}

	err = stmt.QueryRow(phone).Scan(&cm.Id, &cm.Phone, &cm.CreatedDate, &cm.UpdatedDate, &cm.IsPromotion, &cm.PromotionId, &cm.SourceUserId)
	if err != nil {
		return err
	}

	if id != cm.Id {
		return errors.New("用户令牌有误")
	}

	return nil
}